
class NodeModel():
    def __init__(self):
        self.node_num = 0
        self.node_dic = {} # 举例，类型为 {node.id:node}
        
    def newId(self):
        self.node_num +=1
        return self.node_num
    
    def sort(self):
      #   '''
       # 对Node 的顺序进行排序，决定其生成代码的顺序。原则是任何模块的输入端
       # 都必须要比自身的序号小，否则就交换二者的值，相当于冒泡排序。
       # '''
        node_ids = list(self.node_dic.keys())
        
        for i1 in range(len(node_ids)):
            for i in node_ids:
                n = self.node_dic[i]
                for inputNode in n.input_node_list:
                    if inputNode.id > n.id:
                        inputNode.id , n.id = n.id, inputNode.id
        nodeList = list(self.node_dic.values())
        dic = {}
        for n in nodeList:
            dic[n.id] = n
            print(n.id,n.name)
        self.node_dic = dic
        
               
    def show_nodes(self):
        node_ids = list(self.node_dic.keys())
        for i in sorted(node_ids):
            print(i)
            n = self.node_dic[i]
            print(n)
            
    def gen_code(self):
        node_ids = list(self.node_dic.keys())
        code =  'from controller import *\n'
        code +='def process_1(self,event):\n'
        for i in sorted(node_ids):
            n = self.node_dic[i]
            if n.all_port_connected() == True:
                code += n.gen_code()+'\n'
            else:
                print('Warning: ports for node are not all connected.')
                return 
        print(code)
        f = open('/media/hzy/程序/novalide/forgitcommit/scitk/scitk/scitk/widgets/myTries/program/callback.py','w')
        f.write(code)
        f.close()
        
    def gen_controller(self):
        node_ids = list(self.node_dic.keys())
        code = ''
        for i in sorted(node_ids):
            n = self.node_dic[i]
            code += n.gen_func_definition()
        print(code)


class Port():
    def __init__(self,name,direction:str,node=None):
        self.node = node
        self.name = name
        self.line = None
        if direction not in {'input','output'}:
            raise Exception('Unacceptable direction string:\'%s\''%direction)
        self.dir = direction # 'input' 或者'output'
        shape_id = -1 # 可能需要写一下端口数目改变时候“挪位置”的算法了。不过这个是Node的事情。
        text_id = -1 # text_id应当能够通过port反查到内容。代码大致如下：
                            # text_id = node.inputPortList.find_port_by_name('input_port').text_id
                            # canvas.itemconfig(text_id,text = 'aaaaaaaaaaaaa')# 这样修改文字内容即可。
class Line():
    def __init__(self,name,start=None,end=None):
        varName = ''
        
def get_port_by_name(portList, name:str):
    for p in portList:
        if p.name==name:
            return p
    raise Exception('port List %s has no port named \'%s\''%(str(portList),name)) 


nodeModel = NodeModel()
class Node():
    manager = nodeModel# 所有的节点都是一个manager。
    def __init__(self,name,node_id=0,func_name = '', inputVarNames=[],outputVarNames=[]):
        '''
        函数名
        入口参数名
        出口参数名
        
        需要控件信息？self.widget_info<tuple>, 对widget进行命名。注意可以使用tuple 或者是list进行存储，方便json。
        然后还需要进行json的解析。
        参数：type，args(参考生成菜单的部分),name，range等信息。
        
        或者将控件信息独立出来显示和编辑，这就要用到相应的表格参数parameter tree 了。
        '''
        if func_name =='':
            self.func_name = 'func_'+name
        else:
            self.func_name = func_name 
       
        self.input_ports = []
        self.output_ports = []
        for v in inputVarNames:
            self.input_ports.append(Port(v,direction = 'input',node=self))
        for v in outputVarNames:
            self.output_ports.append(Port(v, direction = 'output', node=self))        

        if node_id == 0:
            self.id = self.manager.newId()
        else:
            self.id = node_id
        self.name=name
        self.input_node_list = []
        self.output_node_list = []
        self.manager.node_dic[self.id] = self
        print(self.manager.node_dic)
        
    def all_port_connected(self):
        for p in self.input_ports+self.output_ports:
            if p.line is None:
                return False
        return True
        
    def connect_to(self, node, name_from:str, name_to:str):
        if self not in node.input_node_list:
            node.input_node_list.append(self)
            self.output_node_list.append(node)
            startPort = get_port_by_name(self.output_ports, name_from)
            endPort = get_port_by_name(node.input_ports,  name_to)
            endPort.name = startPort.name
            
    def refresh_ports(self):
        inputNodeSet = set()
        for p in self.input_ports:
            print('line',p.line, self.name)
            if p.line != None:
                inputNodeSet.add(p.line.startPort.node)
                print(inputNodeSet)
        outputNodeSet = set()
        for p in self.output_ports:
            if p.line != None:
                outputNodeSet.add(p.line.endPort.node)
                
        self.input_node_list = list(inputNodeSet)
        self.output_node_list = list(outputNodeSet)
        print(self.name,'in ',self.input_node_list)
        print(self.name,'out ',self.output_node_list)
            
            
    def find_port_by_name(self,name:str,typ='input'):
        if typ == 'input':
            for p in self.input_ports:
                if p.name == name:
                    return p
            raise Exception('\'%s\' is  not in inputList'%(str(name)))
        elif typ == 'output':
            for p in self.output_ports:
                if p.name == name:
                    return p
            raise Exception('%s not in outputList'%(str(name)))
        raise Exception('Invalid port type: %s'%(str(typ)))
        
    def connect_from(self,node):
        node.output_node_list.append(self)
        self.input_node_list.append(node)
        
    def __str__(self):
        return 'name:'+self.name+'\n'+'id:'+repr(self.id)+ '\n'+\
        'input:'+repr(self.input_node_list)+'\n'+'output:'+repr(self.output_node_list)+'\n\n'
    
    def gen_code(self): # 生成代码
        argsStr = ''
        for port in self.input_ports:
            argsStr+='%s=%s,'%(port.name,port.name)
        outputStr ='' 
        for port in self.output_ports:
            outputStr += port.name+ ','
        argsStr = argsStr.strip(',')
        outputStr =  outputStr.strip(',')
        if self.output_ports != []:
            return ' ' * 4 + '%s = %s(%s)'%(outputStr,self.func_name, argsStr)# 空四个格，相当于缩进。
        else:
            return ' ' * 4 + '%s(%s)'%(self.func_name, argsStr)
    
    def gen_func_definition(self):
        argsStr = ''
        for port in self.input_ports:
            argsStr+= '%s,'%(port.name)
        argsStr = argsStr.strip(',')
        
        outputStr ='' 
        for port in self.output_ports:
            outputStr += port.name+ ','
        argsStr = argsStr.strip(',')
        outputStr =  outputStr.strip(',')
        
        return 'def %s(%s):\n    return %s\n\n'%(self.func_name,argsStr, outputStr)
    
    def show_info(self):
        inputPortNames = []
        outputPortNames = []
        for p in self.input_ports:
            inputPortNames.append(p.name)
        for p in self.output_ports:
            outputPortNames.append(p.name)
        return {'input':inputPortNames,'output':outputPortNames,'id':self.id,'name':self.name,'func':self.func_name}
        
            
        
        
if __name__=='__main__':
    a = Node('a',10,outputVarNames = ['ac'])  

    b = Node('b',8 ,outputVarNames = ['bc'])
    c = Node('c',5, inputVarNames = ['ac','bc'], outputVarNames = ['output_c' ,'ce'])
    d = Node('d',11, inputVarNames = ['input_d'])
    e = Node('e',9,inputVarNames = ['ce'])
    a.connect_to(c,'ac','ac')
    b.connect_to(c,'bc','bc')
    c.connect_to(d,'output_c','input_d')# 连接上之后就会自动改名。
    c.connect_to(e,'ce','ce')
##    print(a,b,c,d,e)
    a.manager.sort()
##    a.manager.gen_code()
##    a.manager.gen_controller()
    d=a.show_info()
    import json
    s=json.dumps(d)
    print(d)
    print(json.loads(s))
##    print(a.gen_func_definition()+b.gen_func_definition()+c.gen_func_definition())
##    a.manager.show_nodes()
    
    ##    print(a.id,b.id,c.id,d.id,e.id)
